/***********************************************************************************************************************
**	filename: net_server.c
**	author: Echo Zhang @2021-02-04
**  e-mail: not_xx@126.com
***********************************************************************************************************************/

#include "user_types.h"

#define DBG_NET_SERVER
#if !defined(DBG_NET_SERVER)
#undef DBG_MODE
#endif

#include "dbg.h"
#include "net_server.h"

#include <stdlib.h>
#include <string.h>


static void _process_timer_timeout(uv_timer_t *timer)
{
    NetServer_ST *server = timer->data;
    if(server->targetAddr != NULL) {
        free(server->targetAddr);
        server->targetAddr = NULL;
    }
}
static void _start_timer(NetServer_ST *server)
{
    server->timer.data = server;
    uv_timer_start(&server->timer, _process_timer_timeout, 30*1000, 30*1000);
}

static void _alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
    NetServer_ST *server = handle->data;
    buf->len = server->packetSize;
    buf->base = malloc(buf->len);
    // dbg_msg("malloc: %p, %ld", buf->base, buf->len);
}

static inline bool _addr_equ(const struct sockaddr_in *addr1, const struct sockaddr_in *addr2)
{
    return (addr1->sin_addr.s_addr==addr2->sin_addr.s_addr) && (addr1->sin_port==addr2->sin_port);
}
static void _recv_udp_data(uv_udp_t *udpServer, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags)
{
    NetServer_ST *server = udpServer->data;
    if(addr != NULL) {
        if(server->targetAddr != NULL) {
            if(!_addr_equ((const struct sockaddr_in *)server->targetAddr, (const struct sockaddr_in *)addr)) {
                return;
            }
        } else {
            server->targetAddr = (struct sockaddr *)malloc(sizeof(struct sockaddr));
        }
        memcpy(server->targetAddr, addr, sizeof(struct sockaddr));
        _start_timer(server);
    }
    if(nread > 0) {
        server->recvFunc(server, (const ui8 *)buf->base, nread);
    }
    free(buf->base);
}
bool net_server_init(NetServer_ST *server, NetServer_Recv_FT recvFunc, ui32 packetSize)
{
    server->recvFunc = recvFunc;
    server->packetSize = packetSize;
    uv_loop_t *loop = uv_default_loop();
    uv_timer_init(loop, &server->timer);
    uv_udp_init(loop, &server->udpServer);
    server->udpServer.data = server;

    return true;
}


bool net_server_start(NetServer_ST *server, ui16 port)
{
    server->targetAddr = NULL;
    struct sockaddr_in addr;
    uv_ip4_addr("0.0.0.0", port, &addr);
    uv_udp_bind(&server->udpServer, (const struct sockaddr*)&addr, 0);
    return uv_udp_recv_start(&server->udpServer, _alloc_buffer, _recv_udp_data) == 0;
}


static void _udp_send_result(uv_udp_send_t* req, int status)
{
    // dbg_msg("on send: %p, %d", req, status);
    free(req);
}

void net_server_send_udp_data(NetServer_ST *server, const ui8 data[], Size_DT dataLen)
{
    if(server->targetAddr == NULL) {
        return;
    }
    uv_udp_send_t *req = malloc(sizeof(uv_udp_send_t));
    uv_buf_t buf = uv_buf_init((char *)data, dataLen);
    uv_udp_send(req, &server->udpServer, &buf, 1, server->targetAddr, _udp_send_result);
}

void net_server_stop(NetServer_ST *server)
{
    uv_udp_recv_stop(&server->udpServer);
    uv_timer_stop(&server->timer);
}


/***********************************************************************************************************************
**	endline
***********************************************************************************************************************/
